﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Sustainalytics.DataLayer;
using Sustainalytics.Entities;
using Sustainalytics.Utils;

namespace Sustainalytics.Accounts.BusinessLogic.Tests
{
    [TestClass]
    public class AccountServiceTests
    {
        private static readonly Guid accountId = Guid.NewGuid();

        private readonly ClientUser clientUser = new ClientUser
        {
            Id = Guid.NewGuid(),
            Password = "password",
            PasswordExpirationDate = DateTime.Now
        };


        private readonly List<Account> testedAccounts = new List<Account>
            {
            new Account
            {
                Id = Guid.NewGuid(),
                AccountManager =
                    new AccountManager
                    {
                        Email = "testemail@sustainalytics.com",
                        FirstName = "ManagerFirstName",
                        LastName = "ManagerLastName",
                        Title = "ManagerTitle"
                    },
                ModifiedOn = DateTime.UtcNow,
                Name = "TestAccountName1",
                Office = "Bucharest",
                RelationshipType = RelationshipType.Client,
                Status = Status.Active,
            },
            new Account
            {
                Id = Guid.NewGuid(),
                AccountManager =
                    new AccountManager
                    {
                        Email = "testemail@sustainalytics.com",
                        FirstName = "ManagerFirstName",
                        LastName = "ManagerLastName",
                        Title = "ManagerTitle"
                    },
                ModifiedOn = DateTime.UtcNow,
                Name = "TestAccountName2",
                Office = "Amsterdam",
                RelationshipType = RelationshipType.Client,
                Status = Status.Inactive
            }
        };

        private readonly List<User> testedUsers = new List<User>
         {
            new User
            {
                Id = Guid.NewGuid(),
                Email = "william.zombori@sustainalytics.com",
                Account = "Timisoara",
                LastName = "Zombori",
                FirstName = "William",
                AccountId = accountId,
                Status = Status.Active,
                FullName = "William Zombori",
                ModifiedOn = DateTime.Now
            },
            new User
            {
                Id = Guid.NewGuid(),
                Email = "mihai.stanescu@sustainalytics.com",
                Account = "Arad",
                LastName = "Stanescu",
                FirstName = "Mihai",
                AccountId = Guid.NewGuid(),
                Status = Status.Active,
                FullName = "Mihai Stanescu",
                ModifiedOn = DateTime.Now
            },
            new User
            {
                Id = Guid.NewGuid(),
                Email = "iulian.negoita@sustainalytics.com",
                Account = "Lugoj",
                LastName = "Negoita",
                FirstName = "Iulian",
                AccountId = Guid.NewGuid(),
                Status = Status.Inactive,
                FullName = "Iulian Negoita",
                ModifiedOn = DateTime.Now
            }
         };

        [TestMethod]
        public void GetAccounts()
        {
            var filter = new ListOptions();
            var accountsRepository = new Mock<IAccountsRepository>();
            accountsRepository.Setup(
                m => m.GetAccounts(It.IsAny<Status?>(), It.IsAny<string>(), It.IsAny<ListOptions>(), It.IsAny<bool>()))
                .Returns(
                    (Status? status, string searchText, ListOptions listOptions, bool tier1_or_tier2) =>
            {
                        var returned = new List<Account>(testedAccounts);
                        if (status != null)
                        {
                            returned = returned.Where(a => a.Status == status).ToList();
                        }
                        if (!string.IsNullOrWhiteSpace(searchText))
                        {
                            returned = returned.Where(a => a.Name.ToUpperInvariant().Contains(searchText.ToUpperInvariant())).ToList();
                        }
                        if (listOptions != null)
                        {
                            if (listOptions.OrderBy != null && listOptions.OrderBy.Any())
                            {
                                foreach (var sortOption in listOptions.OrderBy)
                                {
                                    var accountsSortBy =
                                        (AccountsSortEnum) Enum.Parse(typeof(AccountsSortEnum), sortOption, true);

                                    switch (accountsSortBy)
                                    {
                                        case AccountsSortEnum.Name:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(a => a.Name).ToList()
                                                : returned.OrderByDescending(a => a.Name).ToList();
                                            break;
                                        case AccountsSortEnum.RelationshipType:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(a => a.RelationshipType).ToList()
                                                : returned.OrderByDescending(a => a.RelationshipType).ToList();
                                            break;
                                        case AccountsSortEnum.AccountManagerName:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(a => a.AccountManager.FullName).ToList()
                                                : returned.OrderByDescending(a => a.AccountManager.FullName).ToList();
                                            break;
                                        case AccountsSortEnum.Status:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(a => a.Status).ToList()
                                                : returned.OrderByDescending(a => a.Status).ToList();
                                            break;
                                        default:
                                            break;
                                    }
                                }
                            }
                        }
                        return returned.Skip(listOptions.Skip).Take(listOptions.Take).ToList();
                    }
                );

            var accManagement = AccountsManagement.GetInstance(accountsRepository.Object);
            var resultedAccounts = accManagement.GetAccounts(Status.Active, "", filter);

            Assert.IsNotNull(resultedAccounts, "The list of returned accounts is null.");
            Assert.AreEqual(testedAccounts.Count(a => a.Status == Status.Active), resultedAccounts.Count,
                "The number of active accounts is different than the expected one.");
        }

        [TestMethod]
        public void GetAccountsTotalCount()
        {
            var accountsRepository = new Mock<IAccountsRepository>();
            accountsRepository.Setup(m => m.
                GetAccountsTotalCount(It.IsAny<Status?>(), It.IsAny<string>(), It.IsAny<bool>()))
                .Returns((Status? status, string searchText, bool tier1_or_tier2) =>
            {
                    var returned = new List<Account>(testedAccounts);
                    if (status != null)
                    {
                        returned = returned.Where(a => a.Status == status).ToList();
                    }
                    if (!string.IsNullOrWhiteSpace(searchText))
                    {
                        returned = returned.Where(a => a.Name.ToUpperInvariant().Contains(searchText.ToUpperInvariant())).ToList();
                    }

                    return returned.Count;
                }
                );

            var accManagement = AccountsManagement.GetInstance(accountsRepository.Object);
            var resultedCount = accManagement.GetAccountsTotalCount(Status.Active, "");

            Assert.AreEqual(testedAccounts.Count(a => a.Status == Status.Active), resultedCount,
                "The number of accounts is different than the expected one.");
        }

        [TestMethod]
        public void GetAccountById()
        {
            var accountsRepository = new Mock<IAccountsRepository>();
            accountsRepository.Setup(m => m.GetAccountById(It.IsAny<Guid>()))
                .Returns(
                    (Guid id) => testedAccounts.First());

            var accManagement = AccountsManagement.GetInstance(accountsRepository.Object);
            var resultedAccount = accManagement.GetAccountById(testedAccounts.First().Id);
            Assert.IsNotNull(resultedAccount);
            Assert.IsTrue(resultedAccount.Id == testedAccounts.First().Id);
        }

        [TestMethod]
        public void GetUsers()
        {
            var accountsRepository = new Mock<IAccountsRepository>();
            accountsRepository.Setup(
                m =>
                    m.GetUsers(It.IsAny<List<Guid>>(), It.IsAny<Status?>(), It.IsAny<string>(), It.IsAny<ListOptions>()))
                .Returns(
                    (List<Guid> accountIds, Status? status, string searchText, ListOptions listOptions) =>

            {
                        var returned = new List<User>(testedUsers);
                    if (accountIds != null && accountIds.Count > 0)
                    {
                            returned =
                                returned.Where(c => c.AccountId.HasValue && accountIds.Contains(c.AccountId.Value))
                                    .ToList();
                    }
                    if (status != null)
                    {
                        returned = returned.Where(c => c.Status == status).ToList();
                    }
                    if (!string.IsNullOrWhiteSpace(searchText))
                    {
                            returned =
                                returned.Where(
                                    u =>
                                        u.FullName.ToLowerInvariant().Contains(searchText) ||
                                        u.Email.ToLowerInvariant().Contains(searchText) ||
                                        u.Account.ToLowerInvariant().Contains(searchText)).ToList();
                    }
                    if (listOptions != null)
                    {
                        if (listOptions.OrderBy != null && listOptions.OrderBy.Any())
                        {
                            foreach (var sortOption in listOptions.OrderBy)
                            {
                                    var accountsSortBy =
                                        (UsersSortEnum) Enum.Parse(typeof(UsersSortEnum), sortOption, true);

                                switch (accountsSortBy)
                                {
                                    case UsersSortEnum.FullName:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(u => u.FullName).ToList()
                                                : returned.OrderByDescending(u => u.FullName).ToList();
                                        break;
                                    case UsersSortEnum.Email:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(u => u.Email).ToList()
                                                : returned.OrderByDescending(u => u.Email).ToList();
                                        break;
                                    case UsersSortEnum.Account:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(u => u.Account).ToList()
                                                : returned.OrderByDescending(u => u.Account).ToList();
                                        break;
                                    case UsersSortEnum.LastAccessDate:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(u => u.ModifiedOn).ToList()
                                                : returned.OrderByDescending(u => u.ModifiedOn).ToList();
                                                // the field LastAccessDate is not implemented yet
                                        break;
                                    case UsersSortEnum.Status:
                                            returned = listOptions.OrderAscending
                                                ? returned.OrderBy(u => u.Status).ToList()
                                                : returned.OrderByDescending(u => u.Status).ToList();
                                        break;
                                        default:
                                            returned = returned.OrderBy(u => u.FullName).ToList();
                                        break;
                                }
                            }
                        }
                    returned = returned.Skip(listOptions.Skip).Take(listOptions.Take).ToList();
                    }

                    return returned;
                }
                );

            var accManagement = AccountsManagement.GetInstance(accountsRepository.Object);
            var filter = new ListOptions();
            var resultedUsers = accManagement.GetUsers(new List<Guid> {accountId}, null, null, filter);

            Assert.IsNotNull(resultedUsers, "The list of returned users is null.");
            Assert.AreEqual(testedUsers.Where(u => u.AccountId == accountId).Count(), resultedUsers.Count,
                "The number of returned users is different than the expected one.");
        }

        [TestMethod]
        public void GetUsersTotalCount()
        {
            var accountsRepository = new Mock<IAccountsRepository>();
            accountsRepository.Setup(
                m => m.GetUsersTotalCount(It.IsAny<List<Guid>>(), It.IsAny<Status?>(), It.IsAny<string>()))
                .Returns((List<Guid> accountIds, Status? status, string searchText) =>
                {
                    var returned = new List<User>(testedUsers);
                    if (accountIds != null && accountIds.Count > 0)
                    {
                        returned =
                            returned.Where(c => c.AccountId.HasValue && accountIds.Contains(c.AccountId.Value)).ToList();
                    }
                    if (status != null)
                    {
                        returned = returned.Where(c => c.Status == status).ToList();
                    }
                    if (!string.IsNullOrWhiteSpace(searchText))
                    {
                        returned =
                            returned.Where(
                                u =>
                                    u.FullName.ToLowerInvariant().Contains(searchText) || u.Email.ToLowerInvariant().Contains(searchText) ||
                                    u.Account.ToLowerInvariant().Contains(searchText)).ToList();
                    }

                    return returned.Count;
                });
            accountsRepository.Setup(m => m.GetUserByUserId(It.IsAny<Guid>()))
                .Returns(
                    (Guid userId) => testedUsers[0]);

            var accountsManagement = AccountsManagement.GetInstance(accountsRepository.Object);
            var returnedUsers = accountsManagement.GetUsersTotalCount(null, Status.Active, "");

            Assert.IsNotNull(returnedUsers, "The list of returned users is null.");
            Assert.AreEqual(testedUsers.Where(u => u.Status == Status.Active).Count(), returnedUsers,
                "The number of users is different than the expected one.");
        }


        [TestMethod]
        public void GetUserByUserId()
        {
            var accountsRepository = new Mock<IAccountsRepository>();
            accountsRepository.Setup(m => m.GetUserByUserId(It.IsAny<Guid>()))
                .Returns(
                    (Guid userId) => testedUsers[0]);

            var accManagement = AccountsManagement.GetInstance(accountsRepository.Object);
            var user = accManagement.GetUserByUserId(testedUsers[0].Id);

            Assert.IsNotNull(user);
            Assert.IsTrue(user.Status == Status.Active);
        }

        [TestMethod]
        public void GetClientUserByUserId()
        {
            var accountsRepository = new Mock<IAccountsRepository>();
            accountsRepository.Setup(m => m.GetClientUserByUserId(It.IsAny<Guid>()))
                .Returns(
                    (Guid userId) => clientUser);


            var accManagement = AccountsManagement.GetInstance(accountsRepository.Object);
            var user = accManagement.GetClientUserByUserId(clientUser.Id);

            Assert.IsNotNull(user);
            Assert.IsTrue(user.Password == clientUser.Password, "The passwords are not matching.");
        }
    }
}
